前兩天我們介紹了厲害的神燈精靈,以及 JavaScript 這個神燈精靈語的方言。既然我們知道, JavaScript 是用來編寫網頁與使用者互動的腳本;接下來,我們就要開始讓神燈精靈幫助我們操縱網頁中的元素囉!
還記得我們說過,整個網頁中的元素,可以看成一棵橫放的樹,而且我們把這棵樹叫做 "DOM" 嗎?
我們都知道,雖然神燈精靈很萬能,但是它必須接受很明確的指示。我們沒辦法指著網頁上的某個元素,告訴它就是操縱這個元素就對了!因為神燈精靈沒有眼睛,看不到我們手指哪裡。我們必須使用文字描述,告訴它我們希望改變的元素是哪一個。
蝦米,那我們應該怎麼使用 JavaScript 向神燈精靈指出特定元素呢?
我們使用的是 document 這個物件。說是物件聽起來有點嚇人,我們就理解為神秘關鍵字咒語就可以了。當我們對神燈精靈喊 "document" 的時候,它就會知道我們要說的事情和網頁元素有關。事實上,document 也是整棵 DOM 樹的樹根,因此,我們用這個樹根的名稱,來告訴神燈精靈,我們要對網頁元素做些什麼囉!
讓我們先來找到目標元素。找目標元素的方法,和 CSS 選擇器非常類似。我們可能從元素的分堆方式 (id, class, tagname) 來找,也可以從元素的從屬關係來找。例如,讓我們回顧之前的貼紙簿練習:
https://codepen.io/LogosChen/pen/jOwzgpz
在這個練習中,我們可以怎麼找到「粉紅貼紙」的字樣呢?這個字樣被包在 .pink 的元素中;因此,只要我們能夠找到 .pink,就能夠找到這個字樣。找到 .pink 元素的方式非常簡單,先喊出 document 以後,再使用 .getElementsByClassName(類別名稱的文字串) 這個功能,就可以得到屬於這個類別的所有元素構成的陣列。
為什麼我們會獲得陣列呢?因為在網頁中,可能會有許多元素共享同個類別名稱。在我們得到這個元素構成的陣列以後,就可以用陣列項目的方式,把特定的元素挑選出來。也因為這樣,我們這個功能的名稱是 getElement"s"ByClassName。如果是用 id 取得元素,功能名稱就是 getElementById,中間沒有複數的 "s",獲得的結果也是單一元素。
因此,我們要找到「粉紅貼紙」的字樣,就可以使用類別名稱 .pink,來獲得同屬 .pink 類別中的元素所構成的清單,再將這個標籤挑選出來。那麼,我們要怎麼挑出子元素呢?在 JavaScript 中,每個網頁元素都有底下這些屬性:
因此,聰明的阿嬤應該知道怎麼抓出「粉紅元素」這個元素了!我們可以用 getElementsByClassName 這個方法用 .pink 找出屬於 .pink 這個類別的元素陣列,然後選取第 0 項,再挑出 firstChild 就可以了!用 JavaScript 寫看起來會像這樣:
document.getElementsByClassName("pink")[0].firstChild
不過,當我們使用 console.log() 試著印出這個元素時,卻發現完全沒東西!這是怎麼回事呢?原來,瀏覽器會把我們的換行符號也當作 1 個元素。因此,我們必須使用 childNodes[1] 才有辦法找到當中的 p 元素。
document.getElementsByClassName("pink")[0].childNodes[1]
找到特定的元素以後,我們就可以偵測這些元素發生的事件,並且操縱這些元素了。
偵測元素發生事件的方式,是在選定的元素後,加上事件監聽器方法 .addEventListener(事件型態文字串, 執行功能)。事件型態文字串的值非常多,可以參考這份文件。我們這邊只列舉比較常見的:
而執行功能就是任意功能,同樣用 function 字樣帶出。不過,由於這個功能只存在於這個事件監聽器當中,所以不用給它任何名稱。只需要寫成
function(){
}
就可以了喔!
讓我們試試看寫出這個腳本:當使用者點擊「粉紅貼紙」字樣的時候,字樣後方要加上 " Clicked" 字樣。
我們上面已經知道怎麼找到「粉紅貼紙」字樣這個元素,不過由於神燈精靈的記性很差,所以必須也給個變數來儲存這個元素。因此,我們在加上事件監聽器後,整個結構會長這樣:
let pinkText = document.getElementsByClassName("pink")[0].childNodes[1]
pinkText.addEventListener("click",function(){
「粉紅貼紙」字樣加上 "Clicked" 字樣
})
那麼,我們要怎麼把「粉紅貼紙」字樣加上 "Clicked" 字樣呢?我們可能會想,既然我用 console.log(pinkText) 會得到 <p>粉紅貼紙</p> 這個標籤,我們應該就直接寫
pinkText = pinkText + " Clicked"
就可以了吧?奇怪,怎麼完全沒變化呢?因為我們獲得的網頁元素本身是個物件,具備很多屬性,其中的文字內容只是其中一項。當我們想修改其中的文字內容,我們必須修改「文字內容」這個屬性。文字內容這個屬性我們是使用
因此,聰明的阿嬤們應該會知道,我們這邊應該就是需要寫下
pinkText.innerText = pinkText.innerText + " Clicked"
來改變「粉紅貼紙」這個字樣。
是說,各位阿嬤會不會覺得上面這段程式碼也很不 DRY 呢?沒錯,這整個 pinkText.innerText 都重複啦!因此,懶惰注重效率的工程師當然會想節省筆墨囉!每當我們想要寫
變數 = 變數 + 某個值
的時候,我們都可以直接寫成
變數 += 這個值
另外,不只 + 這個運算方式,任何運算方式, -, *, /, %, &&, || 都可以使用這種寫法。因此,當我們想寫
x = x/2
的時候,也可以寫成
x /= 2
甚至,懶惰注重效率的工程師連
x += 1
都懶得寫,而改寫成
x++
;減法也以此類推。
因此,我們就可以使用下面這段 JavaScript,來達成使用者點選「粉紅貼紙」字樣後,再其後加上 " Clicked" 字樣的目標。
let pinkText = document.getElementsByClassName("pink")[0].childNodes[1]
pinkText.addEventListener("click",function(){
pinkText.innerText = pinkText.innerText + " Clicked"
})
最後實現的貼紙簿長這樣:
https://codepen.io/LogosChen/pen/oNeXGRM
各位阿嬤可以自己 fork 回去玩喔!
最後,我們應該怎麼引入我們寫好的 JavaScript 檔呢?讓我們來把在 CodePen 上寫好的檔案抓下來看看吧!我們會發現 index.html 這個檔案的結構長這樣:
<html>
<body>
.
.
.
<script src="./script.js"></script>
</body>
</html>
我們會發現
引入 .js 的位置和引入 .css 的位置不同,是因為 .js 檔通常必須對 DOM 元素做些操作,所以必須等到 DOM 元素都被渲染出來以後, .js 檔當中的腳本才有辦法抓到那些元素,並且加以監聽或操縱。
阿罵這個車速好像有點快啊,瞬間飆過好多JS語法。
哈哈,篇幅和時間有限,會再修正喔~感謝你的一直追蹤!彼此加油喔~
一起加油,恭喜要完賽
謝謝~這篇已經修正囉!